package br.com.fatecjp.pooai.gerenciador.taxi.behaviour;

import jade.core.behaviours.CyclicBehaviour;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;

import java.util.Date;
import java.util.Random;

import br.com.fatecjp.pooai.gerenciador.taxi.agent.AgenteTaxi;
import br.com.fatecjp.pooai.gerenciador.taxi.ambiente.Posicao;
import br.com.fatecjp.pooai.gerenciador.taxi.constants.ConstantesAmbiente;
import br.com.fatecjp.pooai.gerenciador.taxi.message.ConteudoMensagemAgente;
import br.com.fatecjp.pooai.gerenciador.taxi.message.TipoMensagemAgente;
import br.com.fatecjp.pooai.gerenciador.taxi.message.parser.RequisicaoInformeBuscaClienteParser;
import br.com.fatecjp.pooai.gerenciador.taxi.message.parser.RequisicaoNotificacaoMovimentoTaxiParser;
import br.com.fatecjp.pooai.gerenciador.taxi.message.parser.RequisicaoTransporteClienteParser;
import br.com.fatecjp.pooai.gerenciador.taxi.service.NomeServico;

public class MovimentarTaxiBehaviour extends CyclicBehaviour {

	private static final long serialVersionUID = 8367057597558189873L;

	private Random geradorAleatorio;
	private RequisicaoNotificacaoMovimentoTaxiParser reqNotifMovTaxiParser;
	private RequisicaoTransporteClienteParser reqTranspClienteParser;

	private int qtdeMovInvalidos;

	private RequisicaoInformeBuscaClienteParser reqInformeBuscaClienteParser;

	public MovimentarTaxiBehaviour(AgenteTaxi agent) {
		super(agent);
		this.geradorAleatorio = new Random(new Date().getTime());
		this.reqNotifMovTaxiParser = new RequisicaoNotificacaoMovimentoTaxiParser();
		this.reqTranspClienteParser = new RequisicaoTransporteClienteParser();

		this.reqInformeBuscaClienteParser = new RequisicaoInformeBuscaClienteParser();
	}

	private void enviarInformeInteracaoCliente(TipoMensagemAgente tipoMensagem) {

		ServiceDescription descricaoServico = new ServiceDescription();
		descricaoServico.setName(NomeServico.NOTIFICACAO_INTERACAO_CLIENTE
				.toString());

		DFAgentDescription descGerenciador = new DFAgentDescription();
		descGerenciador.addServices(descricaoServico);

		DFAgentDescription[] resultadoBusca = null;
		try {
			resultadoBusca = DFService.search(myAgent, descGerenciador);
		} catch (FIPAException e) {
			throw new RuntimeException(e);
		}

		if (resultadoBusca != null && resultadoBusca.length > 0) {

			descGerenciador = resultadoBusca[0];

			ACLMessage requisicao = new ACLMessage(ACLMessage.INFORM);
			requisicao.setSender(myAgent.getAID());
			requisicao.addReceiver(descGerenciador.getName());

			ConteudoMensagemAgente conteudoMensagem = new ConteudoMensagemAgente(
					tipoMensagem.getCode());
			conteudoMensagem.setConteudo(new Object[] { getAgent()
					.getIdentCliente() });

			requisicao.setContent(this.reqTranspClienteParser
					.toString(conteudoMensagem));

			myAgent.send(requisicao);

		}

	}

	@Override
	public void action() {

		AgenteTaxi agente = this.getAgent();

		ACLMessage mensagemRecebida = myAgent.receive();
		if (mensagemRecebida != null) {

			if (mensagemRecebida.getPerformative() == ACLMessage.INFORM) {

				ConteudoMensagemAgente conteudoMensagem = reqInformeBuscaClienteParser
						.parse(mensagemRecebida.getContent());
				if (conteudoMensagem.getTipoMensagem() == TipoMensagemAgente.REQUISICAO_INFORME_BUSCA_CLIENTE
						.getCode()) {

					agente.setPosicaoCliente((Posicao) conteudoMensagem
							.getConteudo()[0]);
					agente.setPosicaoDestino((Posicao) conteudoMensagem
							.getConteudo()[1]);
					agente.setIdentCliente((String) conteudoMensagem
							.getConteudo()[2]);

				}

			}

		}

		Posicao posicaoAtual = agente.getPosicaoAtual();
		Posicao proximaPosicao = null;

		if (agente.getPosicaoCliente() != null
				|| agente.getPosicaoDestino() != null) {

			/* Movimentar-se em direção ao cliente ou ao seu destino */
			if (agente.getPosicaoCliente() != null) {
				/* Ainda não buscou o cliente */
				if (posicaoAtual.equals(agente.getPosicaoCliente())) {
					this.enviarInformeInteracaoCliente(TipoMensagemAgente.REQUISICAO_INFORME_COLETA_CLIENTE);
					agente.setPosicaoCliente(null);
				} else {
					proximaPosicao = posicaoAtual.obterProximaPosicao(agente
							.getPosicaoCliente());
				}
			} else {
				/*
				 * Pegou o cliente. Está levando-o ao seu destino (ou pode já
				 * ter chegado)
				 */
				if (posicaoAtual.equals(agente.getPosicaoDestino())) {
					this.enviarInformeInteracaoCliente(TipoMensagemAgente.REQUISICAO_INFORME_ENTREGA_CLIENTE);
					agente.setIdentCliente(null);
					agente.setPosicaoDestino(null);
				} else {
					proximaPosicao = posicaoAtual.obterProximaPosicao(agente
							.getPosicaoDestino());
				}
			}

		} else {

			/* Movimentar-se aleatoriamente */

			int xProximaPosicao = 0;
			switch (this.geradorAleatorio.nextInt(3)) {
			case 0:
				xProximaPosicao = posicaoAtual.getX();
				break;
			case 1:
				if (posicaoAtual.getX() < ConstantesAmbiente.NUMERO_POSICOES - 2) {
					xProximaPosicao = posicaoAtual.getX() + 1;
				} else {
					xProximaPosicao = posicaoAtual.getX();
				}
				break;
			case 2:
				if (posicaoAtual.getX() > 0) {
					xProximaPosicao = posicaoAtual.getX() - 1;
				} else {
					xProximaPosicao = posicaoAtual.getX();
				}
				break;
			}

			int yProximaPosicao = 0;
			switch (this.geradorAleatorio.nextInt(3)) {
			case 0:
				yProximaPosicao = posicaoAtual.getY();
				break;
			case 1:
				if (posicaoAtual.getY() < ConstantesAmbiente.NUMERO_POSICOES - 2) {
					yProximaPosicao = posicaoAtual.getY() + 1;
				} else {
					yProximaPosicao = posicaoAtual.getY();
				}
				break;
			case 2:
				if (posicaoAtual.getY() > 0) {
					yProximaPosicao = posicaoAtual.getY() - 1;
				} else {
					yProximaPosicao = posicaoAtual.getY();
				}
				break;
			}

			proximaPosicao = new Posicao(xProximaPosicao, yProximaPosicao);

		}

		if (proximaPosicao != null) {
			if (!proximaPosicao.equals(posicaoAtual)) {

				if (qtdeMovInvalidos > 3) {

					int xProximaPosicao = 0;
					switch (this.geradorAleatorio.nextInt(3)) {
					case 0:
						xProximaPosicao = posicaoAtual.getX();
						break;
					case 1:
						if (posicaoAtual.getX() < ConstantesAmbiente.NUMERO_POSICOES - 2) {
							xProximaPosicao = posicaoAtual.getX() + 1;
						} else {
							xProximaPosicao = posicaoAtual.getX();
						}
						break;
					case 2:
						if (posicaoAtual.getX() > 0) {
							xProximaPosicao = posicaoAtual.getX() - 1;
						} else {
							xProximaPosicao = posicaoAtual.getX();
						}
						break;
					}

					proximaPosicao = new Posicao(xProximaPosicao,
							proximaPosicao.getY());

				}

				ServiceDescription notifMov = new ServiceDescription();
				notifMov.setName(NomeServico.NOTIFICACAO_MOVIMENTO_TAXI
						.toString());

				DFAgentDescription descGerenciador = new DFAgentDescription();
				descGerenciador.addServices(notifMov);

				DFAgentDescription[] resultadoBusca = null;
				try {
					resultadoBusca = DFService.search(agente, descGerenciador);
				} catch (FIPAException e) {
					throw new RuntimeException(e);
				}

				if (resultadoBusca != null && resultadoBusca.length > 0) {

					descGerenciador = resultadoBusca[0];

					ACLMessage informeMovimento = new ACLMessage(
							ACLMessage.INFORM);
					informeMovimento.setSender(agente.getAID());
					informeMovimento.addReceiver(descGerenciador.getName());

					ConteudoMensagemAgente conteudoMensagem = new ConteudoMensagemAgente(
							TipoMensagemAgente.NOTIFICACAO_MOVIMENTO_TAXI
									.getCode());

					conteudoMensagem.setConteudo(new Object[] { posicaoAtual,
							proximaPosicao });

					informeMovimento.setContent(this.reqNotifMovTaxiParser
							.toString(conteudoMensagem));

					agente.send(informeMovimento);

					boolean recebeuResposta = false;

					while (!recebeuResposta) {

						ACLMessage respostaInformeMovimento = agente
								.blockingReceive(5000L);
						if (respostaInformeMovimento != null) {
							recebeuResposta = true;
							if (respostaInformeMovimento.getPerformative() == ACLMessage.CONFIRM) {
								agente.setPosicaoAtual(proximaPosicao);
								qtdeMovInvalidos = 0;
							} else {
								qtdeMovInvalidos++;
							}
						}

					}

				}

			}

		}

		try {
			Thread.sleep(1000L);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

	public AgenteTaxi getAgent() {
		return (AgenteTaxi) myAgent;
	}

}
